/*
 * Created on Mar 23, 2006
 *
 */
package org.mushroomdb.triggers;

import java.util.StringTokenizer;

import org.mushroomdb.catalog.Catalog;
import org.mushroomdb.catalog.Table;
import org.mushroomdb.parser.Parser;
import org.mushroomdb.parser.ParserException;
import org.mushroomdb.parser.ParserService;
import org.mushroomdb.query.Query;
import org.mushroomdb.query.impl.SelectionQuery;
import org.mushroomdb.service.ServiceManager;
import org.mushroomdb.storedprocedures.ExecuteProcedureQuery;

/**
 * CREATE TRIGGER <trigger name> <BEFORE|AFTER> <INSERT|DELETE|UPDATE>
 * ON <relation name> FOR EACH <ROW|STATEMENT>
 * EXECUTE [ QUERY <SQL query> | STATEMENT <statement (args)> ];
 * 
 * DROP TRIGGER <trigger name>
 * 
 * @author mchiodi
 */
public class TriggersParser implements Parser {

	/**
	 * Constructs a TriggersParser.
	 */
	public TriggersParser() {
	}

	/**
	 * @see org.mushroomdb.parser.Parser#parseQuery(java.lang.String)
	 */
	public Query parseQuery(String query) throws ParserException {
		
		try {
			if (query.indexOf("CREATE TRIGGER") > -1 || query.indexOf("create trigger") > -1) {
				return this.parseCreate(query);
			} else {
				return this.parseDrop(query);
			}
		} catch (Exception e) {
			if (e instanceof ParserException) {
				throw (ParserException) e;
			} else {
				throw new ParserException(e);
			}
		}
	}

	/**
	 * Parses CREATE TRIGGER queries
	 * @param query
	 * @return
	 * @throws ParserException
	 */
	private Query parseCreate(String query) throws ParserException {
		String triggerName = null;
		int type;
		int operation;
		Table table = null;
		int mode;
		String executeString = query;
		
		StringTokenizer tokenizer = new StringTokenizer(query);
		
		/* CREATE TRIGGER */
		String token = tokenizer.nextToken();
		if ("CREATE".equals(token)) {
			executeString = executeString.replaceFirst("CREATE","");
		} else if ("create".equals(token)) {
			executeString = executeString.replaceFirst("create","");
		} else {
			throw new ParserException("CREATE expected.");
		}
		token = tokenizer.nextToken();
		if ("TRIGGER".equals(token)) {
			executeString = executeString.replaceFirst("TRIGGER","");
		} else if ("trigger".equals(token)) {
			executeString = executeString.replaceFirst("trigger","");
		} else {
			throw new ParserException("TRIGGER expected.");
		}
		
		/* Trigger's name */
		triggerName = tokenizer.nextToken();
		executeString = executeString.trim();
		executeString = executeString.substring(executeString.indexOf(' '),executeString.length());
		
		/* Type */
		token = tokenizer.nextToken();
		if ("BEFORE".equals(token) || "before".equals(token)) {
			executeString = executeString.trim();
			executeString = executeString.substring(executeString.indexOf(' '),executeString.length());
			type = Trigger.BEFORE;
		} else if ("AFTER".equals(token) || "after".equals(token)) {
			executeString = executeString.trim();
			executeString = executeString.substring(executeString.indexOf(' '),executeString.length());
			type = Trigger.AFTER;
		} else {
			throw new ParserException("BEFORE or AFTER expected.");
		}
		
		/* Operation */
		token = tokenizer.nextToken();
		if ("INSERT".equals(token) || "insert".equals(token)) {
			operation = Trigger.INSERT_OPERATION;
			executeString = executeString.trim();
			executeString = executeString.substring(executeString.indexOf(' '),executeString.length());
		} else if ("DELETE".equals(token) || "delete".equals(token)) {
			operation = Trigger.DLETE_OPERATION;
			executeString = executeString.trim();
			executeString = executeString.substring(executeString.indexOf(' '),executeString.length());
		} else if ("UPDATE".equals(token) || "update".equals(token)) {
			operation = Trigger.UPDATE_OPERATION;
			executeString = executeString.trim();
			executeString = executeString.substring(executeString.indexOf(' '),executeString.length());
		} else {
			throw new ParserException("INSERT, DELETE or UPDATE expected.");
		}
		
		/* ON */
		token = tokenizer.nextToken();
		if ("ON".equals(token)) {
			executeString = executeString.replaceFirst("ON","");
		} else if ("on".equals(token)) {
			executeString = executeString.replaceFirst("on","");
		} else {
			throw new ParserException("ON expected.");
		}
		
		/* Table */
		String tableName = tokenizer.nextToken();
		table = Catalog.getInstance().getTableByName(tableName);
		if (table == null) {
			throw new ParserException("Table " + tableName + " does not exist.");
		}
		executeString = executeString.trim();
		executeString = executeString.substring(executeString.indexOf(' '),executeString.length());
		
		/* FOR EACH */
		token = tokenizer.nextToken();
		if ("FOR".equals(token)) {
			executeString = executeString.replaceFirst("FOR","");
		} else if ("for".equals(token)) {
			executeString = executeString.replaceFirst("for","");
		} else {
			throw new ParserException("FOR expected.");
		}
		token = tokenizer.nextToken();
		if ("EACH".equals(token)) {
			executeString = executeString.replaceFirst("EACH","");
		} else if ("each".equals(token)) {
			executeString = executeString.replaceFirst("each","");
		} else {
			throw new ParserException("EACH expected.");
		}
		
		/* Mode */
		token = tokenizer.nextToken();
		if ("ROW".equals(token) || "row".equals(token)) {
			executeString = executeString.trim();
			executeString = executeString.substring(executeString.indexOf(' '),executeString.length());
			mode = Trigger.ROW_MODE;
		} else if ("STATEMENT".equals(token) || "statement".equals(token)) {
			executeString = executeString.trim();
			executeString = executeString.substring(executeString.indexOf(' '),executeString.length());
			mode = Trigger.STATEMENT_MODE;
		} else {
			throw new ParserException("ROW or STATEMENT expected.");
		}
		
		/* EXECUTE */
		token = tokenizer.nextToken();
		if ("EXECUTE".equals(token)) {
			executeString = executeString.replaceFirst("EXECUTE","");
		} else if ("execute".equals(token)) {
			executeString = executeString.replaceFirst("execute","");
		} else {
			throw new ParserException("EXECUTE expected.");
		}
		
		/* Execute */
		executeString = executeString.trim();
		CreateTriggerQuery createTriggerQuery = null;
		Query executable = null;
		if (executeString.startsWith("QUERY") || executeString.startsWith("query")) {
			/* Query */
			executeString = executeString.trim();
			executeString = executeString.substring(executeString.indexOf(' '),executeString.length());
			ParserService parserService = ParserService.getInstance();
			executable = parserService.getParser().parseQuery(executeString.trim());
		} else if (executeString.startsWith("STATEMENT") || executeString.startsWith("statement")) {
			/* Stored Procedure */
			executeString = executeString.trim();
			executeString = executeString.substring(executeString.indexOf(' '),executeString.length());
			executeString = executeString.replaceAll(";","").trim();
			executable = new ExecuteProcedureQuery(executeString);
		} else {
			throw new ParserException("QUERY or STATEMENT expected.");
		}
		
		createTriggerQuery = new CreateTriggerQuery(
				tableName,
				type,
				operation,
				table,
				mode,
				executable);
		
		return createTriggerQuery;
	}

	/**
	 * Parses DROP TRIGGER queries
	 * @param uQuery
	 * @return
	 * @throws ParserException
	 */
	private Query parseDrop(String uQuery) throws ParserException {
		return null;
	}

	/**
	 * @see org.mushroomdb.parser.Parser#accepts(java.lang.String)
	 */
	public boolean accepts(String query) {
		return query.toUpperCase().indexOf("CREATE TRIGGER") > -1
			||
			query.toUpperCase().indexOf("DROP TRIGGER") > -1;
	}

}
